package com.xdb.sqlparser.dialect.mysql.visitor;

import com.xdb.sqlparser.ast.SQLObject;
import com.xdb.sqlparser.ast.expr.SQLBinaryOpExpr;
import com.xdb.sqlparser.ast.expr.SQLCharExpr;
import com.xdb.sqlparser.ast.expr.SQLHexExpr;
import com.xdb.sqlparser.ast.expr.SQLIdentifierExpr;
import com.xdb.sqlparser.ast.expr.SQLInListExpr;
import com.xdb.sqlparser.ast.expr.SQLIntegerExpr;
import com.xdb.sqlparser.ast.expr.SQLNCharExpr;
import com.xdb.sqlparser.ast.expr.SQLNullExpr;
import com.xdb.sqlparser.ast.expr.SQLNumberExpr;
import com.xdb.sqlparser.ast.expr.SQLPropertyExpr;
import com.xdb.sqlparser.ast.statement.SQLExprTableSource;
import com.xdb.sqlparser.ast.statement.SQLInsertStatement.ValuesClause;
import com.xdb.sqlparser.dialect.mysql.ast.expr.MySqlCharExpr;
import com.xdb.sqlparser.dialect.mysql.ast.statement.MySqlInsertStatement;
import com.xdb.sqlparser.util.ParameterizedOutputVisitorUtils;
import com.xdb.sqlparser.visitor.SQLParameterizedVisitor;

import java.security.AccessControlException;
import java.util.List;
import java.util.Properties;

public class MySqlParameterizedOutputVisitor extends MySqlOutputVisitor implements SQLParameterizedVisitor {

	private boolean shardingSupport = true;

	private int replaceCount;

	public MySqlParameterizedOutputVisitor() {
		this(new StringBuilder());
	}

	public MySqlParameterizedOutputVisitor(Appendable appender) {
		super(appender);

		try {
			configFromProperty(System.getProperties());
		} catch (AccessControlException e) {
		}
	}

	public void configFromProperty(Properties properties) {
		{
			String property = properties.getProperty("parameterized.shardingSupport");
			if ("true".equals(property)) {
				this.setShardingSupport(true);
			} else if ("false".equals(property)) {
				this.setShardingSupport(false);
			}
		}
	}

	public boolean isShardingSupport() {
		return shardingSupport;
	}

	public void setShardingSupport(boolean shardingSupport) {
		this.shardingSupport = shardingSupport;
	}

	public int getReplaceCount() {
		return this.replaceCount;
	}

	public void incrementReplaceCunt() {
		replaceCount++;
	}

	public boolean visit(SQLInListExpr x) {
		return ParameterizedOutputVisitorUtils.visit(this, x);
	}

	public boolean visit(SQLIdentifierExpr x) {
		final String name = x.getName();
		boolean computeSharding = isShardingSupport();
		if (computeSharding) {
			SQLObject parent = x.getParent();
			computeSharding = parent instanceof SQLExprTableSource || parent instanceof SQLPropertyExpr;
		}

		if (computeSharding) {
			int pos = name.lastIndexOf('_');
			if (pos != -1 && pos != name.length() - 1) {
				boolean isNumber = true;
				for (int i = pos + 1; i < name.length(); ++i) {
					char ch = name.charAt(i);
					if (ch < '0' || ch > '9') {
						isNumber = false;
						break;
					}
				}
				if (isNumber) {
					String realName = name.substring(0, pos);
					print(realName);
					incrementReplaceCunt();
					return false;
				}
			}

			int numberCount = 0;
			for (int i = name.length() - 1; i >= 0; --i) {
				char ch = name.charAt(i);
				if (ch < '0' || ch > '9') {
					break;
				} else {
					numberCount++;
				}
			}

			if (numberCount > 1) {
				int numPos = name.length() - numberCount;
				String realName = name.substring(0, numPos);
				print(realName);
				incrementReplaceCunt();
				return false;
			}
		}
		print(name);
		return false;
	}

	public boolean visit(SQLBinaryOpExpr x) {
		x = ParameterizedOutputVisitorUtils.merge(this, x);

		return super.visit(x);
	}

	public boolean visit(SQLIntegerExpr x) {
		if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) {
			return super.visit(x);
		}

		return ParameterizedOutputVisitorUtils.visit(this, x);
	}

	public boolean visit(SQLNumberExpr x) {
		if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) {
			return super.visit(x);
		}

		return ParameterizedOutputVisitorUtils.visit(this, x);
	}

	public boolean visit(SQLCharExpr x) {
		if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) {
			return super.visit(x);
		}

		return ParameterizedOutputVisitorUtils.visit(this, x);
	}

	public boolean visit(SQLNCharExpr x) {
		if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) {
			return super.visit(x);
		}

		return ParameterizedOutputVisitorUtils.visit(this, x);
	}

	public boolean visit(SQLNullExpr x) {
		if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) {
			return super.visit(x);
		}

		return ParameterizedOutputVisitorUtils.visit(this, x);
	}

	public boolean visit(SQLHexExpr x) {
		if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) {
			return super.visit(x);
		}

		return ParameterizedOutputVisitorUtils.visit(this, x);
	}

	public boolean visit(MySqlCharExpr x) {
		if (!ParameterizedOutputVisitorUtils.checkParameterize(x)) {
			return super.visit(x);
		}

		return ParameterizedOutputVisitorUtils.visit(this, x);
	}

	protected void printValuesList(MySqlInsertStatement x) {
		List<ValuesClause> valuesList = x.getValuesList();
		print("VALUES ");
		incrementIndent();
		valuesList.get(0).accept(this);
		decrementIndent();
		if (valuesList.size() > 1) {
			this.incrementReplaceCunt();
		}
	}
}
